package shm;

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.jfree.chart.*;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.*;


public class OfflineWindow {
	
	public static class DataRow {
		public int Source;
		public int Sequence;
		public int Parent;
		public int Etc;
		public int GlobalTime;
		public int X; 
		public int Y;
		public int Z;
		public int E;
	}
	
	
	public static JPanel main;	
	public static JFreeChart jfc;
	
	static ChartPanel cp;
	static XYDataset dataset;

	public static Vector<DataRow> tbl;

	// UI
	public static JComboBox cbOfflineNode;
	public static JLabel labelStat;

	
	public void setup() {
		 //offline
		dataset = createXYDataset();
		jfc = createChart(dataset);
		
		main = new JPanel();
		main.setLayout(null);
		main.setBounds(152, 36, 572, 342);
		cp = new ChartPanel(jfc);
		cp.setBounds(0, 0, 705, 393);
		main.add(cp);

		
		JButton btnOpen = new JButton("OPEN");
		btnOpen.setBounds(34, 415, 94, 30);
		main.add(btnOpen);
		
		
		JButton btnOpenUSB = new JButton("Open USB");
		btnOpenUSB.setBounds(160, 415, 94, 30);
		main.add(btnOpenUSB);
		
		JButton btnFFT = new JButton("FFT");
		btnFFT.setBounds(560, 415, 94, 30);
		main.add(btnFFT);

		JLabel label = new JLabel("Node");
		label.setBounds(301, 418, 50, 27);
		main.add(label);
		
		labelStat = new JLabel("Statistic Information");
		labelStat.setBounds(50, 458, 500, 30);
		main.add(labelStat);

		cbOfflineNode = new JComboBox();
		cbOfflineNode.setBounds(355, 415, 94, 28);
		main.add(cbOfflineNode);
		
		// Event;
		btnOpen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				
				// initialize
				cbOfflineNode.removeAllItems();
				tbl = new Vector<DataRow>();

				String lastPath = MainUI.pref.get("lastPath", "");
				JFileChooser fileopen = new JFileChooser(lastPath);
				fileopen.setMultiSelectionEnabled(false);
				FileNameExtensionFilter filter = new FileNameExtensionFilter(
						"All file", "txt");
				fileopen.addChoosableFileFilter(filter);
				int ret = fileopen.showDialog(null, "Open file");
				if (ret != JFileChooser.APPROVE_OPTION) {
					return ;
				}

				File fileSelected = fileopen.getSelectedFile();
				MainUI.pref.put("lastPath", fileSelected.getParent());
				FileReader fis = null;
				BufferedReader bis = null;
				
				try {
					fis = new FileReader(fileSelected);
					bis = new BufferedReader(fis);
					bis.readLine();
					while (true) {
						String s = bis.readLine();
						if (s == null)
							break;
						
						if (s.length() != (21 * 3))
							continue;
						
						DataRow dr = new DataRow();
						
						dr.Source = 	Logger.hexStringToInt(s, 0, 1);
						dr.Sequence = 	Logger.hexStringToInt(s, 1, 4);
						dr.Parent = 	Logger.hexStringToInt(s, 5, 1);
						dr.Etc = 		Logger.hexStringToInt(s, 6, 2);
						dr.GlobalTime = Logger.hexStringToInt(s, 9, 4);
						dr.X = 			Logger.hexStringToInt(s, 13, 2);
						dr.Y = 			Logger.hexStringToInt(s, 15, 2);
						dr.Z = 			Logger.hexStringToInt(s, 17, 2);
						dr.E = 			Logger.hexStringToInt(s, 19, 2);
						
						tbl.add(dr);
					}

					bis.close();
					fis.close();
				} catch (Exception edd) {
					edd.printStackTrace();
				}
				
				// add all id to a sorted set and then insert into dropdownlist
				TreeSet<Integer> ids = new TreeSet<Integer>();
				for (DataRow d : tbl)
					ids.add(d.Source);
				
				for (Integer i : ids)
					cbOfflineNode.addItem(i);
				
				dataset = createXYDataset();
				jfc = createChart(dataset);
				cp.setChart(jfc);
				cp.repaint();
			}
		});
		
		// Open USB
		btnOpenUSB.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// initialize
				cbOfflineNode.removeAllItems();
				tbl = new Vector<DataRow>();

				String lastPath = MainUI.pref.get("lastPath", "");
				JFileChooser fileopen = new JFileChooser(lastPath);
				fileopen.setMultiSelectionEnabled(false);
				FileNameExtensionFilter filter = new FileNameExtensionFilter(
						"All file", "txt");
				fileopen.addChoosableFileFilter(filter);
				int ret = fileopen.showDialog(null, "Open file");
				if (ret != JFileChooser.APPROVE_OPTION) {
					return ;
				}

				File fileSelected = fileopen.getSelectedFile();
				MainUI.pref.put("lastPath", fileSelected.getParent());
				
				try {
					FileInputStream fis = new FileInputStream(fileSelected);
					byte [] buf = new byte[(int) fileSelected.length()];
					fis.read(buf);
					int idx = 0;
					int seq = 0;
					while (idx + 12 < buf.length) {
						DataRow dr = new DataRow();
						dr.Source = 	2;
						dr.Sequence = 	seq++;
						dr.Parent = 	0;
						dr.Etc = 		0;
						dr.GlobalTime = Logger.intFromBytesLE(buf, idx, 4);
						idx += 4;
						dr.X = 			Logger.intFromBytesLE(buf, idx, 2);
						idx += 2;
						dr.Y = 			Logger.intFromBytesLE(buf, idx, 2);
						idx += 2;
						dr.Z = 			Logger.intFromBytesLE(buf, idx, 2);
						idx += 2;
						dr.E = 			Logger.intFromBytesLE(buf, idx, 2);
						idx += 2;
						
						// jason : add only 65535
						if (seq <= 655356)
							tbl.add(dr);
					}
					fis.close();
				} catch (Exception edd) {
					edd.printStackTrace();
				}
				
				// add all id to a sorted set and then insert into dropdownlist
				TreeSet<Integer> ids = new TreeSet<Integer>();
				for (DataRow d : tbl)
					ids.add(d.Source);
				
				for (Integer i : ids)
					cbOfflineNode.addItem(i);
				
				dataset = createXYDataset();
				jfc = createChart(dataset);
				cp.setChart(jfc);
				cp.repaint();
			}
		});
		
		// Open USB
		btnFFT.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				dataset = createFFTDataset();
				jfc = createChart(dataset);
				cp.setChart(jfc);
				cp.repaint();
			}
		});

		 // Open offline
		cbOfflineNode.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent evt) {
				if (evt.getStateChange() == ItemEvent.SELECTED) 
				{
					dataset = createXYDataset();
					jfc = createChart(dataset);
					cp.setChart(jfc);
					cp.repaint();
				}
			}
		});


	}
	
	
	public static JFreeChart createChart(XYDataset dataset) {

		JFreeChart jfreechart = ChartFactory.createXYLineChart("", 
				"Time", 
				"Value",
				dataset, // dataset
				PlotOrientation.VERTICAL,
				true, true, true);

		XYPlot plot = (XYPlot) jfreechart.getPlot();
		plot.getRenderer().setSeriesStroke(0, new BasicStroke(2f));
		plot.setDomainGridlinePaint(Color.white);
		plot.setRangeGridlinePaint(Color.white);
		ValueAxis domain = plot.getDomainAxis();
		domain.setTickLabelPaint(Color.white);
		ValueAxis range = plot.getRangeAxis();
		range.setTickLabelPaint(Color.white);

		jfreechart.clearSubtitles();
		jfreechart.setBackgroundPaint(Color.black);
		XYPlot xyplot = (XYPlot) jfreechart.getPlot();
		xyplot.setBackgroundPaint(Color.black);

		ValueAxis ca = xyplot.getRangeAxis();
		ca.setAutoRange(true);
		ValueAxis ca2 = xyplot.getDomainAxis();
		ca2.setAutoRange(true);
		// plot.setOutlineStroke(new BasicStroke(2.5f));
		plot.setBackgroundAlpha(0.5f);
		// plot.setBackgroundPaint(Color.black);
		plot.setForegroundAlpha(0.5f);
		
		XYItemRenderer renderer = plot.getRenderer();
		renderer.setSeriesPaint(0, Color.RED);
		renderer.setSeriesPaint(1, Color.GREEN);
		renderer.setSeriesPaint(2, Color.BLUE);
		renderer.setSeriesPaint(3, Color.WHITE);

		return jfreechart;
	}
	
	private static XYDataset createFFTDataset() {
		Vector<Integer> ev = new Vector<Integer>();
		
		XYSeries e = new XYSeries("FFT");
		
		if (cbOfflineNode == null)
			return new XYSeriesCollection();
		
		// get id and axis 
		int id = (Integer)cbOfflineNode.getSelectedItem();
		
		for(DataRow dr : tbl){
			if (dr.Source != id)
				continue;
			ev.add(dr.E);
		}
		// FFT
		int N = 32768;
        Complex[] x = new Complex[N];

        for (int i = 0; i < N; i++) {
            x[i] = new Complex(ev.get(i).intValue(), 0);
        }
        
        // Minus the average value.
        double avg = Logger.Avg(x);
        for (int i = 0; i < N; i++) {
        	 x[i] = x[i].minus(new Complex(avg, 0));
        }

        // FFT of original data
        Complex[] y = FFT.fft(x);
        
        for(int i = 0; i < y.length; i++) {
        	e.addOrUpdate(i, y[i].abs());
        }

		XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
		xySeriesCollection.addSeries(e);

		return xySeriesCollection;
	}


	private static XYDataset createXYDataset() {
		
		Vector<Integer> ev = new Vector<Integer>();
		
		//XYSeries x = new XYSeries("X");
		//XYSeries y = new XYSeries("Y");
		//XYSeries z = new XYSeries("Z");
		XYSeries e = new XYSeries("E");
		
		if (cbOfflineNode == null || cbOfflineNode.getSelectedItem() == null)
			return new XYSeriesCollection();
		
		// get id and axis 
		int id = (Integer)cbOfflineNode.getSelectedItem();
		
		for(DataRow dr : tbl){
			if (dr.Source != id)
				continue;
			
			//x.addOrUpdate(dr.Sequence, dr.X);
			//y.addOrUpdate(dr.Sequence, dr.Y);
			//z.addOrUpdate(dr.Sequence, dr.Z);
			ev.add(dr.E);
		}
		
		int [] da = new int[ev.size()];
		for(int i = 0; i < da.length; i++) { 
			da[i] = ev.get(i).intValue();
		}
		// Subtract the average
		double avg = Logger.Avg(da);
		for(int i = 0; i < ev.size(); i++) {
			e.addOrUpdate(i, da[i]-avg);
		}

		XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
		// Jason: Uncomment for one axis
		//xySeriesCollection.addSeries(x);
		//xySeriesCollection.addSeries(y);
		//xySeriesCollection.addSeries(z);
		xySeriesCollection.addSeries(e);
		
		
		// update statistic
		int [] ds = new int[ev.size()];
		for(int i = 0; i < ds.length; i++)
			ds[i] = ev.get(i).intValue();
		
		String disp = String.format("Statistic Info: %d items. Average = %f. Variance = %f.", 
				ds.length, Logger.Avg(ds), Logger.Variance(ds));

		labelStat.setText(disp);

		return xySeriesCollection;
	}

}
